// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include <entities/WorldEntity.h>
#include <entities/WorldEntityHeader.h>
#include <map>
#include <unordered_map>
#include <unordered_set>

/**
 * A pool of entities. Drivers exchange the pool with the manager.
 */
class FWorldEntityManagerPool
{

public:

	using Ptr = std::shared_ptr<FWorldEntityManagerPool>;
	using EntitySet = std::unordered_set<FWorldEntity::Ptr>;
	using EntityMap = std::unordered_map<std::int64_t, FWorldEntity::Ptr>;
	using TypeMapping = std::map<std::string, EntitySet>;
	using IdMapping = std::map<std::string, EntityMap>;

	TypeMapping	TypeMappings;
	IdMapping	IdMappings;

	FWorldEntityHeader::Ptr	Header;

public:

	void Add(const FWorldEntity::Ptr& entity);

	void Remove(const FWorldEntity::Ptr& entity);

	template<typename EntityType>
	EntitySet& GetTypeMapping()
	{
		return TypeMappings[EntityType::sMetaClass().ClassName];
	}

	template<typename EntityType>
	EntityMap& GetIdMapping()
	{
		return IdMappings[EntityType::sMetaClass().ClassName];
	}

};

/**
 * Base class for the entity manager drivers.
 * We have 2 implementations for now: remote WorldDB and local OpenDRIVE file.
 */
class FWorldEntityManagerDriver
{

public:

	using Ptr = std::shared_ptr<FWorldEntityManagerDriver>;

public:

	virtual ~FWorldEntityManagerDriver()
	{}

	virtual void Connect(const std::string& path) = 0;

	virtual int GetRevision(int world) = 0;

	virtual FWorldEntityManagerPool::Ptr GetRegion(double E, double W, double N, double S, int world, int rev) = 0;

	virtual void SetRegion(const FWorldEntityManagerPool::Ptr& pool, 
					const std::unordered_set<FWorldEntity::Ptr>& newEntities,
					const std::unordered_set<FWorldEntity::Ptr>& dirtyEntities,
					const std::unordered_set<FWorldEntity::Ptr>& removedEntities,
					int world, int rev) = 0;

public:

	class Exception : public FWorldException
	{
	public:
		Exception(const char* what)
			: FWorldException(what)
		{}
		Exception(const std::string& what)
			: FWorldException(what)
		{}
	};

};
